﻿using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Net;
using System.Text;
using System.Web;
using Newtonsoft.Json;


namespace BariumLiveAPIClient
{
	/// <summary>
	/// This code example demonstrates how to send messages to Process Applications and Process Instances using the Barium Live REST API.
	/// Note that the code is designed to support version 2012.1.4 of Barium Live and later versions. An upcoming version 2012.3 will reduce the number of API calls required (see comments in the code).
	/// </summary>
	class Program
	{
		static void Main(string[] args)
		{
			if (Environment.CommandLine.Contains("-?") || Environment.CommandLine.Contains("/?") || args.Length < 9)
			{
			    Console.WriteLine("BariumLiveAPIClient.exe url apikey username password appid message instancename templateid parameters [instanceid]");
			    Console.WriteLine("parameters should be on the format field1=value1&field2=value2");
			    Console.WriteLine();
			    Console.WriteLine(String.Format("You supplied too few arguments ({0} out of 9)", args.Length));
			}
			else
			{
				string url = args[0].TrimEnd('/') + "/api/v1.0";
				string apikey = args[1];
				string username = args[2];
				string password = args[3];
				string appid = args[4];
				string message = args[5];
				string instancename = args[6];
				string templateid = args[7];	// NOTE! Parameter not required in Barium Live 2012.1.5 and later
				string parameters = args[8];

				string instanceid = null;
				if (args.Length == 10)
				{
					instanceid = args[9];
				}

				string response = null;
				string ticket = null;

				response = InvokeAPI(url + "/authenticate", "POST", "apikey=" + apikey + "&username=" + username + "&password=" + password);

				if (String.IsNullOrEmpty(response) && response.ToLower().Contains("failed"))
				{
					Console.WriteLine("Authentication failed");
					return;
				}
				else
				{
					ticket = response;
				}

				if (ticket != null)
				{
					// Get current account information - NOTE! Not required in Barium Live 2012.1.5 and later
					response = InvokeAPI(String.Format("{0}/account/current", url), "GET", "", ticket);
					AccountEntity currentAccount = JsonConvert.DeserializeObject<AccountEntity>(response);

					// Additional parameters
					Dictionary<string, string> parametersDictionary = new Dictionary<string, string>();
					parametersDictionary.Add("instancename", instancename);
					parametersDictionary.Add("template", templateid);								// NOTE! Parameter not required in Barium Live 2012.1.5 and later. Will be replaced by the DataObject Data ID that is configured in the process model.
					parametersDictionary.Add("instance.initiator", currentAccount.SecurityId);		// NOTE! Parameter not required in Barium Live 2012.1.5 and later

					// Parse parameters string
					foreach (string item in parameters.Split('&'))
					{
						if (String.IsNullOrEmpty(item))
							continue;

						string[] kvp = item.Split('=');
						parametersDictionary.Add(kvp[0], kvp[1]);
					}

					// Should we send a message to an existing instance?
					if (instanceid != null)
					{
						// Send a message to an existing process instance
						response = InvokeAPI(String.Format("{0}/instances/{1}?message={2}", url, instanceid, message), "POST", EncodeParameters(parametersDictionary), ticket);
						Console.WriteLine("The API returned: " + response);

						// NOTE! Lines below not required in Barium Live 2012.1.5 and later

						// First, get a list of all objects in the instance
						response = InvokeAPI(String.Format("{0}/instances/{1}/objects", url, instanceid), "GET", "", ticket);
						JsonResultEntity<ObjectEntity> objects = JsonConvert.DeserializeObject<JsonResultEntity<ObjectEntity>>(response);

						// Find object by template id
						ObjectEntity objectEntity = objects.Data.FirstOrDefault(o => o.TemplateID == templateid);
						if (objectEntity != null)
							response = InvokeAPI(String.Format("{0}/objects/{1}/fields", url, objectEntity.Id), "POST", EncodeParameters(parametersDictionary), ticket);
					}
					else
					{
						// Send a message to process app
						response = InvokeAPI(String.Format("{0}/apps/{1}?message={2}", url, appid, message), "POST", EncodeParameters(parametersDictionary), ticket);

						Console.WriteLine("The API returned: " + response);
						return;
					}
				}
				else
				{
					Console.WriteLine("An unknown error has occured");
					return;
				}
			}
		}


		#region Helpers

		static string InvokeAPI(string url, string method, string parameters, string ticket = null)
		{
			HttpWebRequest request = (HttpWebRequest)HttpWebRequest.Create(url);
			request.ContentType = "application/x-www-form-urlencoded";

			request.Method = method;

			if (ticket != null)
				request.Headers.Add("ticket", ticket);

			if (method.ToUpper() == "POST" && parameters != null)
			{
				byte[] bytes = Encoding.UTF8.GetBytes(parameters);

				Stream requestStream = null;
				try
				{
					request.ContentLength = bytes.Length;
					requestStream = request.GetRequestStream();
					requestStream.Write(bytes, 0, bytes.Length);
				}
				finally
				{
					if (requestStream != null)
						requestStream.Close();
				}
			}

			// Get the response
			HttpWebResponse response = (HttpWebResponse)request.GetResponse();

			if (response == null)
				return null;

			StreamReader sr = new StreamReader(response.GetResponseStream());

			return sr.ReadToEnd().Trim();
		}


		static string EncodeParameters(Dictionary<string, string> parameters)
		{
			StringBuilder encodedParameters = new StringBuilder();

			foreach (string parameter in parameters.Keys)
			{
				if (encodedParameters.Length > 0)
					encodedParameters.Append("&");

				encodedParameters.Append(parameter).Append("=").Append(HttpUtility.UrlEncode(parameters[parameter]));
			}

			return encodedParameters.ToString();
		}

		#endregion
	}
}